package pt.inescid.components.policyengine.heimdall.pap.base;

import java.util.Vector;

import pt.inescid.components.policyengine.heimdall.base.EventObj;
import pt.inescid.components.policyengine.heimdall.base.User;
import pt.inescid.components.policyengine.heimdall.commons.Utilities;
import pt.inescid.components.policyengine.heimdall.pdp.PolicyDecision;

public class CompositeDefinition extends RuleDefinition {

//  ----------------------------------------------------------
//  ------------------------   variables   ------------------------
//  ----------------------------------------------------------
	private Vector operands;
	protected PolicyDecision decision;

//  ----------------------------------------------------------
//  ------------------------   constructors   ------------------------
//  ----------------------------------------------------------
	public CompositeDefinition(int type,  
			RuleDefinition previousLevel) {
		super(type, previousLevel);
		operands = new Vector();
	}

	public void addOperand(RuleDefinition newOperand) {
		this.operands.add(newOperand);
	}

	public Vector getOperands() {
		return operands;
	}
	
//  ----------------------------------------------------------
//  ------------------------   toString   ------------------------
//  ----------------------------------------------------------
	public String toString() {
		return (toString(0));
	}

//  ----------------------------------------------------------
//  ------------------------   toString   ------------------------
//  ----------------------------------------------------------
	public String toString(int tabAdvance) {
		String result = "";
		
		for (int i = 0; i < tabAdvance; i++) {
			result += "\t";
		}
		
		result += RULE_TYPE_CODES[type] + " w/ " + operands.size() + " operands.\n";
		for (int i = 0; i < operands.size(); i++) {
			result += ((RuleDefinition) operands.elementAt(i)).toString(tabAdvance+1) + "\n";
		}
		
		return (result);
	}

	//  ----------------------------------------------------------
//  ------------------------   getFunction   ------------------------
//  ----------------------------------------------------------
	public String getFunction() {
		String result = "";
		RuleDefinition currentOperand = null;
		RuleDefinition nextOperand = null;
		
		for (int i = 0; i < operands.size(); i++) {
			currentOperand = (RuleDefinition) operands.elementAt(i);
			result += currentOperand.getFunction();
		};
		
		result += 
"		boolean " + getTemporaryVar() + " = ";
		
		for (int i = 0; i < operands.size(); i++) {
			currentOperand = (RuleDefinition) operands.elementAt(i);
		
			if (i != (operands.size()-1)) {
				nextOperand = (RuleDefinition) operands.elementAt(i+1);
			}
			
			if (type == LOGICAL_NOT_OPERATOR) {
				result += "!" + currentOperand.getTemporaryVar();
			}
			else if (i != (operands.size()-1)) {
				switch (type) {
				case LOGICAL_AND_OPERATOR:
					result += currentOperand.getTemporaryVar() + 
							" && " +
							nextOperand.getTemporaryVar();
					break;
				case LOGICAL_OR_OPERATOR:
					result += currentOperand.getTemporaryVar() + 
						" || " +
						nextOperand.getTemporaryVar();
					break;
				case EQUALS_OPERATOR:
					if (currentOperand.isNumeric()) {
						result += currentOperand.getTemporaryVar() + 
								" == " +
								nextOperand.getTemporaryVar();
					}
					else {
						result += currentOperand.getTemporaryVar() + 
							".equals(" +
							nextOperand.getTemporaryVar() + ")";
					}
					break;
				case GREATER_THAN_OPERATOR:
					result += currentOperand.getTemporaryVar() + 
					" > " +
					nextOperand.getTemporaryVar();
					break;
				case LESSER_THAN_OPERATOR:
					result += currentOperand.getTemporaryVar() + 
					" < " +
					nextOperand.getTemporaryVar();
					break;

				default:
					break;
				}
			}
		}
		
		result += ";\n";
		
		return (result);
	}

//  ----------------------------------------------------------
//  ------------------------   isNumeric   ------------------------
//  ----------------------------------------------------------
	public boolean isNumeric() {
		return (false);
	}

//  ----------------------------------------------------------
//  ------------------------   isPolicyComposition   ------------------------
//  ----------------------------------------------------------
	public boolean isPolicyComposition() {
		for (int i = 0; i < operands.size(); i++) {
			if (((RuleDefinition) operands.elementAt(i)).isPolicyComposition()) {
				return (true);
			}
		}
		
		return (false);
	}
	
//  ----------------------------------------------------------
//  ------------------------   getParameters   ------------------------
//  ----------------------------------------------------------
	public Vector getParameters() {
		Vector allParameters = new Vector();
		Vector currentParameters = null;
		RuleDefinition currentOperand = null;
		
		for (int i = 0; i < operands.size(); i++) {
			currentOperand = (RuleDefinition) operands.elementAt(i);
			currentParameters = currentOperand.getParameters();
			
			for (int j = 0; j < currentParameters.size(); j++) {
				allParameters.add((String) currentParameters.elementAt(j));				
			}
		}
		
		return (allParameters);
	} // getParameters

	
//  ----------------------------------------------------------
//  ------------------------   getRelevantEventFields   ------------------------
//  ----------------------------------------------------------
	public Vector getRelevantEventFields() {
		Vector allRelevant = new Vector();
		
		Vector currentRelevant = null;
		RuleDefinition currentOperand = null;
		for (int i = 0; i < operands.size(); i++) {
			currentOperand = (RuleDefinition) operands.elementAt(i);
			currentRelevant = currentOperand.getRelevantEventFields();
			Utilities.joinParametersNoDuplicates(allRelevant, currentRelevant);
		}
		
		return (allRelevant);
	} // getRelevantEventFields

	
//  ----------------------------------------------------------
//  ------------------------   getRelevantCes   ------------------------
//  ----------------------------------------------------------
	public Vector getRelevantCes() {
		Vector allRelevant = new Vector();
		
		Vector currentRelevant = null;
		RuleDefinition currentOperand = null;
		for (int i = 0; i < operands.size(); i++) {
			currentOperand = (RuleDefinition) operands.elementAt(i);
			currentRelevant = currentOperand.getRelevantCes();
			Utilities.joinCesNoDuplicates(allRelevant, currentRelevant);
		}
		
		return (allRelevant);
	} // getRelevantCes


	
	
	public PolicyDecision getDecision() {
		return decision;
	}

	public void setDecision(PolicyDecision decision) {
		this.decision = decision;
	}

	public void setOperands(Vector operands) {
		this.operands = operands;
	}

//  ----------------------------------------------------------
//  ------------------------   getTimeIndependentCopy   ------------------------
//  ----------------------------------------------------------
	public RuleDefinition getTimeIndependentCopy() {
		CompositeDefinition result = new CompositeDefinition(
				type, 
				previousLevel);
		RuleDefinition currentOperand = null;
		for (int i = 0; i < operands.size(); i++) {
			currentOperand = 
				((RuleDefinition) operands.elementAt(i)).
				getTimeIndependentCopy();
			
			// if any operand is dependent on the time, then discard it
			// Only other operands in an "AND" condition will remain
			if (currentOperand == null) {
				if (type != RuleDefinition.LOGICAL_AND_OPERATOR) {
					return (null);
				}
			}
			else {
				result.addOperand(currentOperand);
			}
		}
		
		if (result.getOperands().size() == 0) {
			return (null);
		}
		else if (result.getOperands().size() == 1) {
			return ((RuleDefinition) result.getOperands().elementAt(0));
		}
		else {
			return (result);
		}
	} // getTimeIndependentCopy

	
	
//  ----------------------------------------------------------
//  ------------------------   getObligation   ------------------------
//  ----------------------------------------------------------
	public RuleDefinition getObligation() {
		CompositeDefinition result = new CompositeDefinition(
				type, 
				previousLevel);
		RuleDefinition currentOperand = null;
		for (int i = 0; i < operands.size(); i++) {
			currentOperand = 
				(RuleDefinition) operands.elementAt(i);
			
			if (!(currentOperand.getType() ==
				RuleDefinition.LOGICAL_AND_OPERATOR) ||				
				currentOperand.isObligationDependent()) {
				
				result.addOperand(currentOperand.getObligation());
			}
		}
		
		if (result.getOperands().size() == 0) {
			return (null);
		}
		else if (result.getOperands().size() == 1) {
			return ((RuleDefinition) result.getOperands().elementAt(0));
		}
		else {
			return (result);
		}
	} // getObligation

	
//  ----------------------------------------------------------
//  ------------------------   getObligationIndependentCopy   ------------------------
//  ----------------------------------------------------------
	public RuleDefinition getObligationIndependentCopy() {
		CompositeDefinition result = new CompositeDefinition(
				type, 
				previousLevel);
		RuleDefinition currentOperand = null;
		for (int i = 0; i < operands.size(); i++) {
			currentOperand = 
				(RuleDefinition) operands.elementAt(i);
			
			if (currentOperand.isObligationDependent()) {
				if (type != RuleDefinition.LOGICAL_AND_OPERATOR) {
					return (null);
				}
			}
			else {
				result.addOperand(currentOperand);
			}
		}
		
		if (result.getOperands().size() == 0) {
			return (null);
		}
		else if (result.getOperands().size() == 1) {
			return ((RuleDefinition) result.getOperands().elementAt(0));
		}
		else {
			return (result);
		}
	} // getObligationIndependentCopy

	
//  ----------------------------------------------------------
//  ------------------------   getCeIndependentCopy   ------------------------
//  ----------------------------------------------------------
	public RuleDefinition getCeIndependentCopy() {
		CompositeDefinition result = new CompositeDefinition(
				type, 
				previousLevel);
		RuleDefinition currentOperand = null;
		for (int i = 0; i < operands.size(); i++) {
			currentOperand = 
				((RuleDefinition) operands.elementAt(i)).
				getCeIndependentCopy();
			
			// if any operand is dependent on 'ce' (the current event), 
			// than discard it
			// Only other operands in an "AND" condition will remain
			if (currentOperand == null) {
				if (type != RuleDefinition.LOGICAL_AND_OPERATOR) {
					return (null);
				}
			}
			else {
				result.addOperand(currentOperand);
			}
		}
		
		if (result.getOperands().size() == 0) {
			return (null);
		}
		else if (result.getOperands().size() == 1) {
			return ((RuleDefinition) result.getOperands().elementAt(0));
		}
		else {
			return (result);
		}
	} // getCeIndependentCopy

	
//  ----------------------------------------------------------
//  ------------------------   isObligationDependent   ------------------------
//  ----------------------------------------------------------
	public boolean isObligationDependent() {

		RuleDefinition currentOperand = null;
		for (int i = 0; i < operands.size(); i++) {
			currentOperand = 
				(RuleDefinition) operands.elementAt(i);
			
			if (currentOperand.isObligationDependent()) {
				return (true);
			}
		}
		
		return (false);
	} // isObligationDependent


	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 ============= getSqlRestriction =====================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
	public String getSqlRestriction(
			User currentUser, EventObj ce) {
		
		String restriction = "";
		RuleDefinition currentOperand = null;
//		  ----------------------------------------------------------
//		  ----------------------------------------------------------
		for (int i = 0; i < operands.size(); i++) {
			currentOperand = (RuleDefinition) operands.elementAt(i);
			
			if (type == RuleDefinition.LOGICAL_NOT_OPERATOR) {
				restriction += "NOT ";
			}
			else if (!restriction.equals("")) {
				switch (type) {
				case RuleDefinition.LOGICAL_AND_OPERATOR:
					restriction += " AND ";
					break;					
				case RuleDefinition.LOGICAL_OR_OPERATOR:
					restriction += " OR ";
					break;					
				case RuleDefinition.EQUALS_OPERATOR:
					restriction += "=";
					break;					
				case RuleDefinition.GREATER_THAN_OPERATOR:
					restriction += ">";
					break;					
				case RuleDefinition.LESSER_THAN_OPERATOR:
					restriction += "<";
					break;					

				case RuleDefinition.ADD_OPERATOR:
					restriction += "+";
					break;					
				case RuleDefinition.SUBTRACT_OPERATOR:
					restriction += "-";
					break;					
				case RuleDefinition.MULTIPLY_OPERATOR:
					restriction += "*";
					break;					
				case RuleDefinition.DIVIDE_OPERATOR:
					restriction += "/";
					break;					
				default:
					break;
				}
			}
			
			restriction += 
				currentOperand.getSqlRestriction(currentUser, ce);
			
		}
	
		return (restriction);
	} // getSqlRestriction

	
	
	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 ============= getValueType =====================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
	public int getValueType() {
		
		switch (type) {
		case RuleDefinition.LOGICAL_AND_OPERATOR:
		case RuleDefinition.LOGICAL_OR_OPERATOR:
		case RuleDefinition.LOGICAL_NOT_OPERATOR:
		case RuleDefinition.EQUALS_OPERATOR:
		case RuleDefinition.GREATER_THAN_OPERATOR:
		case RuleDefinition.LESSER_THAN_OPERATOR:
			
			return (RuleDefinition.BOOLEAN_VALUE);

		case RuleDefinition.ADD_OPERATOR:
		case RuleDefinition.SUBTRACT_OPERATOR:
		case RuleDefinition.MULTIPLY_OPERATOR:
		case RuleDefinition.DIVIDE_OPERATOR:
			
			return (((RuleDefinition) 
					operands.elementAt(0)).getValueType());

		default:
			return (RuleDefinition.STRING_VALUE);
		}
	}

	
}; // CompositeExpression
